package com.lengxf.milo.listener;


import cn.hutool.core.collection.CollectionUtil;
import com.lengxf.milo.callBack.DataValueListenerCallBack;
import com.lengxf.milo.callBack.DefaultDataValueListenerCallBack;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.UaClient;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscriptionManager;
import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedDataItem;
import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedItem;
import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedSubscription;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class ReSubscriptionListener implements UaSubscriptionManager.SubscriptionListener {


    private final DataValueListenerCallBack callBack;

    private final ManagedSubscription subscription;

    public ReSubscriptionListener(UaClient client, List<NodeId> nodeIdList) throws UaException {
        this(client, nodeIdList, new DefaultDataValueListenerCallBack());
    }

    public ReSubscriptionListener(UaClient client, List<NodeId> nodeIdList, DataValueListenerCallBack callBack) throws UaException {
        this.callBack = callBack;
        this.subscription = ManagedSubscription.create((OpcUaClient) client, 1000.0);
        this.subscription.setDefaultSamplingInterval(1000.0);
        this.subscription.setDefaultQueueSize(UInteger.valueOf(10));
        this.subscription.createDataItems(nodeIdList);
        refreshDataListener();
    }


    @Override
    public void onSubscriptionTransferFailed(UaSubscription subscription, StatusCode statusCode) {
        refreshDataListener();
    }

    private void refreshDataListener() {
        subscription.getDataItems().forEach(dataItem -> dataItem.addDataValueListener(callBack::consume));
    }

    public void addDataItemsListener(List<NodeId> nodeIdList) throws UaException {
        List<NodeId> exitsNodeIdList = subscription.getDataItems().stream().map(ManagedItem::getNodeId).collect(Collectors.toList());
        List<NodeId> addList = nodeIdList.stream().filter(e -> notExitsNodeId(exitsNodeIdList, e)).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(addList)) {
            subscription.createDataItems(nodeIdList).forEach(dataItem -> dataItem.addDataValueListener(callBack::consume));
        }
    }

    public void removeDataItemsListener(List<NodeId> nodeIdList) throws UaException {
        List<ManagedDataItem> dataItems = subscription.getDataItems();
        List<ManagedDataItem> collect = dataItems.stream().filter(e -> !notExitsNodeId(nodeIdList, e.getNodeId())).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(collect)) {
            subscription.deleteDataItems(collect);
        }
    }

    private static Boolean notExitsNodeId(List<NodeId> nodeIdList, NodeId nodeId) {
        return nodeIdList.stream().filter(e -> {
            String ns = e.getNamespaceIndex().toString();
            String identifier = e.getIdentifier().toString();
            String ns1 = nodeId.getNamespaceIndex().toString();
            String identifier1 = nodeId.getIdentifier().toString();
            return ns1.equals(ns) && identifier1.equals(identifier);
        }).findFirst().isEmpty();
    }

}
